package cn.z2huo.demo.spring.webmvc.controller.sse;

import cn.hutool.core.util.RandomUtil;
import cn.z2huo.demo.spring.webmvc.entity.dto.sse.SSEReqDTO;
import cn.z2huo.demo.spring.webmvc.entity.dto.sse.SSESendDataDTO;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.http.MediaType;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>Servlet 的 Server-Sent Events (SSE) 测试
 *
 * @author z2huo
 *
 * @see SseEmitter
 * @see org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter
 */
@RestController
@RequestMapping("/sse")
@Slf4j
public class SSETestController {

    @PostMapping(path = "/test", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter simpleTest(@RequestBody SSEReqDTO reqDTO) {
        log.info("receive message: {}", reqDTO);

        SseEmitter sseEmitter = getSseEmitter();

        log.info(" test method end");
        return sseEmitter;
    }

    @GetMapping(path = "/test", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter simpleTest2() {

        SseEmitter sseEmitter = getSseEmitter();

        log.info(" test2 method end");
        return sseEmitter;
    }

    @GetMapping(value = "custom", produces = "text/event-stream;charset=UTF-8")
    public SseEmitter custom(HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("UTF-8");

        SseEmitter sseEmitter = new SseEmitter(60 * 1000L);

        Thread thread = new Thread(() -> {
            long threadId = Thread.currentThread().threadId();

            try {
                sseEmitter.send(SseEmitter.event().name("ready").id("12").comment("comment hhh")
                        .data(Map.of("thread-id", threadId), MediaType.APPLICATION_JSON));

                FileReader fileTxt = new FileReader(ResourceUtils.getFile("classpath:sse/sseReturnFile.txt"));
                BufferedReader bufferedReader = new BufferedReader(fileTxt);
                try (bufferedReader){
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        int length = line.length();
                        if (length == 0) {
                            continue;
                        }
                        int splitCount = RandomUtil.randomInt(1, length / 10 == 0 ? 1 : length / 10, true, true);

                        List<Integer> splitIndexList = new ArrayList<>(splitCount);
                        int min = 0, max = length - 1;
                        for (int i = 0; i < splitCount; i++) {
                            int index = RandomUtil.randomInt(min, max, false, true);
                            min = index;
                            splitIndexList.add(index);
                            if (min == max) {
                                break;
                            }
                        }
                        if (max != length) {
                            splitIndexList.add(length);
                        }

                        int lastIndex = 0;
                        for (Integer index : splitIndexList) {
                            String substring = line.substring(lastIndex, index);
                            sseEmitter.send(SseEmitter.event().name("message").id(RandomStringUtils.randomAlphabetic(4))
                                    .data(new SSESendDataDTO().setV(substring), MediaType.APPLICATION_JSON)
                            );
                            lastIndex = index;
                        }
                        TimeUnit.MILLISECONDS.sleep(200);
                    }
                    sseEmitter.complete();
                }
            } catch (Exception e) {
                log.error("send message error", e);
                sseEmitter.completeWithError(e);
                throw new RuntimeException(e);
            }
        });

        thread.start();

        return sseEmitter;
    }

    private SseEmitter getSseEmitter() {
        SseEmitter sseEmitter = new SseEmitter(60 * 1000L);

        Thread thread = new Thread(() -> {
            try {
                for (int i = 0; i < 50; i++) {
                    String sendValue = "send object " + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
                    sseEmitter.send(new SSESendDataDTO().setV(sendValue));
                    TimeUnit.MILLISECONDS.sleep(500);
                }
                sseEmitter.complete();
            } catch (Exception e) {
                log.error("send message error", e);
                sseEmitter.completeWithError(e);
                throw new RuntimeException(e);
            }
        });
        thread.start();
        return sseEmitter;
    }

}
